/******************************************************************************
 * Author:      671643387
 * Created:     2016/9/29
 *****************************************************************************/

var mUtil = require('./util.js');
var mFunc = require('./function.js');
var ErrorCodes = require('./error.js').ErrorCodes;
var mjMark = require('./mjmark.js');
///////////////////////////////////////////////////////////////////////////////

var MAX_CARDS = 27;

/**
 * 玩家手牌
 * @constructor
 */
function HandCards() {
    this.num = 0;           // 当前牌总数
    this.cards = [];        // 玩家手牌

    this.init();
}
HandCards.prototype = {
    // 初始化
    init: function() {
        for (var iCard = 0; iCard < MAX_CARDS; ++iCard) {
            this.cards[iCard] = {
                value : iCard + 1,
                num : 0,
            };
        }
        this.num = 0;
    },

    // 添加手牌
    adds: function(cards) {
        var _me = this;
        cards.forEach(function(card){
            var value = (card.type - 1) * 9 + card.value;
            var newCard = _me.cards[value - 1];
            newCard.num += 1;
            _me.cards[value - 1] = newCard;
            _me.num += 1;
        });
    },

    // 增加手牌
    add: function(card, num) {
        num = num || 1;
        var myCard = this.cards[card.value - 1];
        myCard.num += num;
        this.num += num;
    },

    // 获取手牌有几个和这张牌一样的牌
    hasSame: function(card) {
        var myCard = this.cards[card.value - 1];
        return myCard.num;
    },

    findMark: function (pMark) {
        var flag = false;
        this.marks.forEach(function (mark) {
            if(mark.key == pMark && mark.value == 10){
                flag = true;
                return;
            }
        });
        return flag;
    },

    // 减少手牌
    decCard: function(card, num) {
        var myCard = this.cards[card.value - 1];
        myCard.num -= num;
        this.num -= num;
    },

    // 获取手牌同步数据
    getSyncData: function() {
        return this.cards;
    },

    // 获取还剩余几张手牌
    getRemainCardNum: function() {
        return this.num;
    },

    // 获取剩余的卡片
    getRemainCards: function() {
        var cards = [];
        this.cards.forEach(function(card){
            if (card.num != 0) {
                cards.push(card);
            }
        });
        return cards;
    },
};

/**
 * 麻将玩家
 * @constructor
 */
function Player(owner) {
    this.owner          = owner;      // 玩家所在房间
    this.uid            = 0;          // 玩家编号
    this.integral       = 1000;       // 玩家积分
    this.tempIntegarl   = 0;          // 当前局积分
    this.conn           = null;       // 连接对象
    this.ip             = "";         // 玩家IP
    this.player         = null;       // 玩家信息
    this.mahjongs       = new HandCards();       // 玩家手牌
    this.newMahjong     = { value: 0 };       // 玩家刚摸的牌
    this.online         = false;      // 是否在线
    this.hangPackets    = [];         // 挂起等待发送的数据包

    this.pengs          = [];         // 玩家碰的牌
    this.gangs          = [];         // 玩家杠的牌
    this.gangMarks      = [];         // 杠牌标志

    this.marks          = [];         // 鸡、豆标志
    this.settlementMarks= {};         // 加、扣分类型 ： 加、扣分数量

    this.isTingPai      = false;      // 玩家是听牌
    this.isHuPai        = false;      // 玩家是否胡牌
    this.isBaoTing      = false;      // 玩家是否报听
    this.isKillBao      = false;      // 玩家是否杀报
    this.isFirstCard    = true;       // 当前局第一张牌
    this.huPaiCard      = null;       // 胡的牌
    this.huPaiType      = [];         // 胡牌类型
    this.huPaiMethod    = [];         // 胡牌方式
    this.huPaiScore     = 0;          // 胡牌分数
    this.isErrorHu      = false;          // 错胡标志

    this.tips           = {
        gang: 0,
        peng: 0,
        hu: 0,
        baoting: 0,
    };

    this.isCurGang      = false;      // 玩家是否正杠牌
    this.isGangFirstCard    = false;    // 是否杠牌后出的第一个张牌
}
Player.prototype = {

    /**
     * 初始化玩家数据
     * @param conn      连接对象
     * @param uid       玩家编号
     * @param player    玩家数据
     */
    init: function(conn, uid, player) {
        this.uid = uid;
        this.conn = conn;
        this.player = player;

        // 麻将玩家
        this.conn.player = this;
        this.ip = this.conn.remoteAddress;

        // 设置玩家在线
        this.online = true;
    },

    // 玩家继续下一局，重置数据
    continue: function() {
        this.mahjongs.init();
        this.newMahjong = { value: 0 };
        this.pengs = [];
        this.gangs = [];
        this.marks = [];
        this.settlementMarks = {};
        this.isTingPai = false;
        this.isHuPai = false;
        this.huPaiCard = null;
        this.huPaiType = [];
        this.huPaiMethod = [];
        this.huPaiScore = 0;
        this.clearTips();
        this.isCurGang = false;
        this.isGangFirstCard = false;
        this.tempIntegarl = 0;
        this.isBaoTing = false;
        this.isKillBao = false;
        this.isFirstCard = true;
        this.isErrorHu = false;
    },

    // 获取同步数据
    getSyncData: function(mini) {
        var syncData = {};

        if (mini) {
            syncData = {
                info: this.getInfo(),        // 玩家名字，头像等数据
                integral: this.integral,            // 当前分数
                cards: [],          // 手牌
                card: {value:0},              // 刚摸的牌
                gangs: [],                       // 杠的牌
                pengs: [],                       // 碰的牌
                cardNum: 0   // 剩余手牌张数
            };
        } else {
            var gangs = [];
            for (var key in this.gangs) {
                if (!this.gangs.hasOwnProperty(key)) {
                    continue;
                }

                gangs.push({
                    value: this.gangs[0],
                    mark: this.gangMarks[+key],
                });
            }

            var pengs = [];
            this.pengs.forEach(function(peng){
                pengs.push(peng[0]);
            });
            syncData = {
                info: this.getInfo(),        // 玩家名字，头像等数据
                integral: this.integral,            // 当前分数
                cards: this.mahjongs.getSyncData(), // 手牌
                card: this.newMahjong,              // 刚摸的牌
                gangs: gangs,                       // 杠的牌
                pengs: pengs,                       // 碰的牌
                cardNum: this.mahjongs.getRemainCardNum()   // 剩余手牌张数
            };
        }

        return syncData;
    },

    // 该玩家断线了
    onDisconnect: function() {
        this.online = false;
        this.owner.onPlayerDisconnect(this);
    },

    // 玩家重连
    onReconnect: function() {
        // 发送缓存的数据包给该玩家
        if (this.hangPackets.length > 0) {
            this.hangPackets.forEach(function(packet){
                this.sendPacket(packet.act, packet.msg);
            }.bind(this));
            this.hangPackets = [];
        }
    },

    isOnline: function() {
        return this.online;
    },

    pushHuPaiMethod: function(method) {
        this.huPaiMethod.push(method);
    },
    getHuPaiMethod: function() {
        return this.huPaiMethod;
    },
    hasHuPaiMethod: function(method) {
        return (this.huPaiMethod.indexOf(method) != -1);
    },
    clearHuPaiMethod: function() {
        this.huPaiMethod = [];
    },

    pushHuPaiType: function(type) {
        this.huPaiType.push(type);
    },
    getHuPaiType: function() {
        return this.huPaiType;
    },
    hasHuPaiType: function(type) {
        return (this.huPaiType.indexOf(type) != -1);
    },
    clearHuPaiType: function() {
        this.huPaiType = [];
    },

    setHuPaiScore: function(score) {
        this.huPaiScore = score;
    },

    decIntegral: function(what, num) {
        this.integral -= num;
        this.tempIntegarl -= num;

        if (!this.settlementMarks.hasOwnProperty(what)) {
            this.settlementMarks[what] = 0;
        }
        this.settlementMarks[what] -= num;
    }
    ,
    incIntegral: function(what, num) {
        this.integral += num;
        this.tempIntegarl += num;
        if (!this.settlementMarks.hasOwnProperty(what)) {
            this.settlementMarks[what] = 0;
        }
        this.settlementMarks[what] += num;
    },

    incHuPaiScore:function (winScore) {
        this.huPaiScore += winScore;
    },

    decHuPaiScore:function (failScore) {
        this.huPaiScore -= failScore;
    },

    getSettlementInfo: function() {

        var jiScore = this.owner.subGame.onSettlementJitotalAction(this);
        var gangScore = this.owner.subGame.onSettlementGangtotalAction(this);
        var tingCard = 0;
        if(this.tips.hu != 0){
            tingCard = this.tips.hu;
        }
        return {
            isHu: this.isHuPai,
            isTing: this.isTingPai,
            integral: this.integral,
            tempintegarl:this.tempIntegarl,
            cards: this.getSettlementCards(),
            huPaiMethod: this.huPaiMethod[0],
            huPaiType: this.huPaiType[0],
            huPaiScore: this.huPaiScore,
            jiScore: jiScore,
            gangScore: gangScore,
            tingCard: tingCard,
        };
    },

    // 玩家是否听牌
    isTing: function() {
        return this.isTingPai;
    },
    // 检查玩家是否听牌
    checkTingPai: function() {
        if (this.isHuPai) {
            this.isTingPai = true;
            return;
        }

        if (mFunc.checkHearPai(this)) {
            this.isTingPai = true;
        } else {
            this.isTingPai = false;
        }
    },

    /**
     * 这个数据是否已经初始化
     */
    isInited: function () {
        return this.player != null;
    },

    isPlayer: function(uid) {
        return this.uid == uid;
    },

    /**
     * 设置手牌
     * @param cards
     */
    setCards: function(cards) {
        this.mahjongs.adds(cards);
        this.sendPacket('setCards', this.mahjongs.getSyncData());
    },

    /**
     * 设置刚摸的牌
     * @param card
     */
    setNewMahjong: function(card) {
        this.newMahjong = {
            value: (card.type - 1) * 9 + card.value
        };

        this.isErrorHu = false;

        if (this.checkHupai(this.newMahjong, true, this)) {
            this.tips.hu = this.newMahjong.value;
        }
        this.tips.gang = this.checkGang(this.newMahjong, true);

        this.sendPacket('setNewCard', {
            card: this.newMahjong,
        });

        if(this.isFirstCard){
            // 起牌后摸的第一张牌，检查是否有报听
            this.mahjongs.add(this.newMahjong,1);
            for (var pos in this.mahjongs.cards){
                if(!this.mahjongs.cards.hasOwnProperty(pos)){
                    continue;
                }
                if(this.mahjongs.cards[pos].num == 0){
                    continue;
                }
                this.mahjongs.cards[pos].num -= 1;

                if(mFunc.checkHearPai(this)){
                    this.tips.baoting = 1;
                    this.mahjongs.cards[pos].num += 1;
                    break;
                }
                this.mahjongs.cards[pos].num += 1;
            }
            this.mahjongs.decCard(this.newMahjong,1);

            this.isFirstCard = false;
        }
        this.sendTips();
    },

    hasTip: function(name) {
        return this.tips[name] != 0;
    },

    hasAnyTip: function() {
        return this.tips.gang != 0 || this.tips.peng != 0 || this.tips.hu != 0;
    },

    clearTips: function() {
        this.tips.gang = 0;
        this.tips.peng = 0;
        this.tips.hu = 0;
        this.tips.baoting = 0;
    },

    isCurGangCard: function() {
        this.isCurGang;
    },

    getBaoTing: function(){
        this.isBaoTing;
    },

    setBaoTing: function () {
        this.isBaoTing = true;
    },

    getKillBao: function(){
        this.isKillBao;
    },

    setKillBao: function () {
        this.isKillBao = true;
    },

    getGangs: function() {
        return this.gangs;
    },

    getPengs: function() {
        return this.pengs;
    },

    // 发送提示
    sendTips: function(isHu) {
        isHu = isHu || false;

        var tips = [];
        if (!isHu) {
            if (this.tips.gang != 0) {
                tips.push('gangpai');
            }
            if (this.tips.peng != 0) {
                tips.push('pengpai');
            }
        }
        if (this.tips.hu != 0) {
            tips.push('hupai');
        }
        if (this.tips.baoting != 0){
            tips.push('baoting');
        }
        if (tips.length > 0) {
            this.sendPacket('showTips', {
                tips: tips,
            });
        }
    },

    /**
     * 玩家出牌
     * @param card
     * @returns {boolean}
     */
    playCard: function(card) {
        if (card.value == this.newMahjong.value) {
            // 打的是刚摸的牌
            this.newMahjong.value = 0;
            return true;
        } else {
            // 检查玩家有没这张牌
            var numSame = this.mahjongs.hasSame(card);
            if (numSame == 0) {
                return false;
            }

            // 减少一张出的手牌
            this.mahjongs.decCard(card, 1);

            if (this.newMahjong.value != 0) {
                // 把摸得牌放进去
                this.mahjongs.add(this.newMahjong);
                this.newMahjong.value = 0;
            }

            if (this.isCurGang) {
                this.isGangFirstCard = true;
            } else {
                this.isGangFirstCard = false;
            }
            this.isCurGang = false;
            return true;
        }
    },

    baoTing: function(){
        if(!this.getBaoTing() && this.tips.baoting ) {
            this.setBaoTing();

            this.tips.baoting = 0;

            this.sendTips();

            return true;
        }
        return false;
    },

    gangCard: function(card) {
        // 检查能否杠牌
        if (this.tips.gang == 0) {
            return ErrorCodes.ERROR_PLAYER_CAN_NOT_GANG;
        }

        // 杠牌
        if (this.newMahjong.value != 0) {

            // 检查碰的牌
            var flag = false;
            for (var iPeng = 0; iPeng < this.pengs.length; ++iPeng) {
                var pengs = this.pengs[iPeng];
                if (pengs[0] == this.newMahjong.value) {
                    flag = true;
                }
            }
            var flagMark = ( !this.isFirstCard && this.newMahjong.value != this.tips.gang );
            if(flag){ //加杠
                // 将碰牌中的牌删除
                this.delPeng(this.newMahjong);
                // 将摸的牌和碰牌中的牌加入杠
                this.addGang(this.newMahjong, mjMark.PLAY_MARK_ADD_GANG);

                if(flagMark){
                    this.markPush(mjMark.PLAY_MARK_HAN_BAO_GANG, this.newMahjong.value);
                }else {
                    this.markPush(mjMark.PLAY_MARK_ADD_GANG, this.newMahjong.value);
                }

                this.newMahjong.value = 0;
            }else{  //暗杠
                // 将摸的牌和手牌中的牌加入杠
                this.addGang(this.newMahjong, mjMark.PLAY_MARK_DARK_GANG);

                this.mahjongs.decCard({value:this.tips.gang}, 3);

                if(flagMark){
                    this.markPush(mjMark.PLAY_MARK_HAN_BAO_GANG, this.newMahjong.value);
                }else {
                    this.markPush(mjMark.PLAY_MARK_DARK_GANG, this.newMahjong.value);
                }
                this.newMahjong.value = 0;
            }

        } else {
            // 杠别人的牌
            this.addGang(card, mjMark.PLAY_MARK_APPOINT_GANG);

            this.mahjongs.decCard(card, 3);

            this.markPush(mjMark.PLAY_MARK_APPOINT_GANG, card.value);
        }

        // 清除提示
        this.clearTips();

        // 玩家刚杠了牌
        this.isCurGang = true;

        return 0;
    },

    pengCard: function (card) {
        // 检查能否碰牌
        if(this.tips.peng == 0){
            return ErrorCodes.ERROR_PLAYER_CAN_NOT_GANG;
        }
        //碰牌
        this.addPeng(card);

        this.mahjongs.decCard(card, 2);

        // 清除提示
        this.clearTips();

        return 0;
    },

    huCard: function (card) {
        if (this.huPaiType.length == 0) {
            // 玩家不能胡牌
            return ErrorCodes.ERROR_PLAYER_CAN_NOT_HUPAI;
        }

        this.isHuPai = true;
        this.isTingPai = true;

        return 0;
    },

    pass: function() {

    },

    /**
     * 根据其他玩家当前出的牌检查自己能否碰/杠等
     * @param card      牌
     * @param sPlayer   出牌的玩家
     */
    checkWithCurMahjong: function(card, sPlayer) {
        //玩家有杠牌通行证才检查是否胡牌
        if( !this.isErrorHu ) {
            if (this.checkHupai(card, false, sPlayer)) {
                this.tips.hu = card.value;
            }
        }
        this.tips.gang = this.checkGang(card, false);

        if (this.checkPeng(card, true)) {
            this.tips.peng = card.value;
        }
    },

    hasPengs: function() {
        return this.pengs.length > 0;
    },

    hasGangs: function() {
        return this.gangs.length > 0;
    },

    isHu: function() {
        return this.isHuPai;
    },

    getHandCards: function() {
        return this.mahjongs;
    },

    getSettlementCards: function() {
        var handCards = clone(this.mahjongs);
        this.gangs.forEach(function(gang){
            handCards.add({value: gang[0]}, 4);
        });
        this.pengs.forEach(function(peng){
            handCards.add({value: peng[0]}, 3);
        });
        return handCards.getRemainCards();
    },

    addGang: function(card, mark) {
        this.gangs.push([card.value, card.value, card.value, card.value]);
        this.gangMarks.push(mark);
    },

    addPeng: function (card) {
        this.pengs.push([card.value, card.value, card.value]);
    },

    delPeng: function (card) {
        for (var iPeng = 0; iPeng < this.pengs.length; ++iPeng) {
            var pengs = this.pengs[iPeng];
            if (pengs[0] == card.value) {
                this.pengs.splice(iPeng,1);
                break;
            }
        }
    },

    markPush: function (mark,card) {
        this.marks.push({
            key:mark,
            value:card
        });
    },

    markDel:function (num) {
        this.pengs.splice(iPeng,1);
        this.marks.splice(this.marks.length - 1, num);
    },
    delMark:function (index, num) {
        this.marks.splice(index,num);
    },

    getLastMark: function() {
        return (this.marks[this.marks.length - 1]).key;
    },

    addHuPai: function (card) {
        this.huPaiCard = card.value;
    },
    ///////////////////////////////////////////////////////////////////////////
    //>> 功能函数

    // 发送数据包
    sendPacket: function(act, content) {
        if (!this.online) {
            // 玩家不在线时将推送给他的数据缓存起来等待发送
            this.hangPackets.push({
                act: act,
                msg: content,
            });

            return;
        }

        var resp = {
            code: 0,
            desc: '',
            mod: 'api',
            act: act,
            data: content
        };
        DEBUG(util.format("Send %s to %d, %j", act, this.uid, content));
        this.conn.sendUTF(JSON.stringify(resp));
    },

    // 返回给其他玩家展示的数据
    getInfo: function() {
        if (!this.player) {
            return this.player;
        }

        var info = clone(this.player);
        info.uid = this.uid;
        info.ip = this.ip;
        return info;
    },

    // 检查自摸
    checkZimong: function() {
        return true;
    },

    // 检查能否碰牌
    checkPeng: function(card) {
        return (this.mahjongs.hasSame(card) >= 2);
    },

    // 检查能否杠牌
    checkGang: function(card, me) {
        // 检查手牌
        if (this.mahjongs.hasSame(card) == 3) {
            return card.value;
        }

        if (me) {
            // 检查碰的牌
            for (var iPeng = 0; iPeng < this.pengs.length; ++iPeng) {
                var pengs = this.pengs[iPeng];
                if (pengs[0] == card.value) {
                    return card.value;
                }
            }

            //检查手牌中有没有杠牌
            for (var key in this.mahjongs.cards) {
                if (!this.mahjongs.cards.hasOwnProperty(key)) {
                    continue;
                }

                if (this.mahjongs.cards[key].num == 4) {
                    return this.mahjongs.cards[key].value;
                }
            }
        }

        return 0;
    },

    // 检查胡牌
    checkHupai: function(card, me, sPlayer) {
        // 能否胡牌
        this.clearHuPaiMethod();
        this.clearHuPaiType();
        var canHePai = mFunc.checkHupai(this, card, me);
        if (canHePai) {
            if (this.owner.subGame.onCheckPlayerHuPai(this, me)) {
                if (me) {
                    if (this.isCurGang) {
                        // 杠开
                        this.pushHuPaiMethod(mjMark.HUCARD_METHOD_GANGKAI);
                    } else {
                        // 自摸
                        this.pushHuPaiMethod(mjMark.HUCARD_METHOD_ZIMO);
                    }
                } else {
                    // 别人点炮

                    if (sPlayer.isGangFirstCard) {
                        // 热炮
                        this.pushHuPaiMethod(mjMark.HUCARD_METHOD_REPAO);
                    } else {
                        this.pushHuPaiMethod(mjMark.HUCARD_METHOD_DIANPAO);
                    }
                }
                return true;
            }
        }
        return false;
    }
};
exports.Player = Player;

